package us.mooresr.webtroopmanager.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpSession;

import us.mooresr.webtroopmanager.client.TroopManagerService;
import us.mooresr.webtroopmanager.shared.Event;
import us.mooresr.webtroopmanager.shared.EventDriver;
import us.mooresr.webtroopmanager.shared.EventDriverSummary;
import us.mooresr.webtroopmanager.shared.EventParticipant;
import us.mooresr.webtroopmanager.shared.EventParticipantSummary;
import us.mooresr.webtroopmanager.shared.EventSummary;
import us.mooresr.webtroopmanager.shared.Person;
import us.mooresr.webtroopmanager.shared.PersonSummary;
import us.mooresr.webtroopmanager.shared.Privilege;
import us.mooresr.webtroopmanager.shared.Role;
import us.mooresr.webtroopmanager.shared.Unit;
import us.mooresr.webtroopmanager.shared.User;
import us.mooresr.webtroopmanager.shared.UserSummary;
import us.mooresr.webtroopmanager.shared.Vehicle;
import us.mooresr.webtroopmanager.shared.VehicleSummary;

import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;

public class TroopManagerServiceImpl extends RemoteServiceServlet implements TroopManagerService {

	private static final Logger log = Logger.getLogger(TroopManagerServiceImpl.class.getName());
	private static final long serialVersionUID = 1L;
	
	@Override
	public void init() {
		log.fine("Initialize starts.");
		ObjectifyService.register(Person.class);
		ObjectifyService.register(Vehicle.class);
		ObjectifyService.register(Event.class);
		ObjectifyService.register(Role.class);
		ObjectifyService.register(Unit.class);
		ObjectifyService.register(User.class);
		ObjectifyService.register(EventDriver.class);
		ObjectifyService.register(EventParticipant.class);
		log.fine("Initialize done.");
	}

	@Override
	public PersonSummary addPerson(Person p) {
		Objectify ofy = ObjectifyService.begin();
		ofy.put(p);
		
		PersonSummary ps = new PersonSummary(p.getId(), p.getFirstName(), p.getLastName());
		return ps;
	
	}
	
	@Override
	public Long deletePerson(Long key) {
		Objectify ofy = ObjectifyService.begin();
		ofy.delete(Person.class, key);
		return key;
		
	}

	@Override
	public List<PersonSummary> getAllPeople() {
		List<PersonSummary> list = new ArrayList<PersonSummary>();
		Objectify ofy = ObjectifyService.begin();
		
		
		Query<Person> allPeople = ofy.query(Person.class).order("lastName");
		
		for (Person p : allPeople) {
			list.add(new PersonSummary(p.getId(), p.getFirstName(), p.getLastName()));
		}
		
		
		return list;
	}
	
	@Override
	public List<PersonSummary> getPeople(Set<Long> keys) {
		List<PersonSummary> list = new ArrayList<PersonSummary>();
		Objectify ofy = ObjectifyService.begin();
		
		if (keys == null) {
			return null;
		}
		
		Map<Long, Person> personMap = ofy.get(Person.class, keys);
		
		for (Person p : personMap.values()) {
			list.add(new PersonSummary(p.getId(), p.getFirstName(), p.getLastName()));
		}
		
		
		return list;	
	}
	
	@Override
	public Person getPerson(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		Person p = ofy.get(Person.class, key);
		
		return p;
	}
	
	/**
	 * Import People from the people.txt resource.  Deletes all existing people.
	 * 
	 * Input format:
	 * id,firstName,lastName,type,medformA,medformB,medformC,driverLicense,active,gender,dob,homePhone,workPhone,cellPhone,
	 * address,city,state,zip,email
	 * 
	 * SQL command to generate file:   select * from people_with_detail into outfile '/tmp/people.txt' FIELDS TERMINATED BY ',' 
	 *        LINES TERMINATED BY '\n' ;
     */

	
	@Override
	public Integer preloadData() {
		Objectify ofy = ObjectifyService.begin();
		
		InputStream in = getServletContext().getResourceAsStream("/people.txt");
		if (in == null) return -1;
		
		InputStreamReader isr = new InputStreamReader(in);
		if (isr == null) return -2;
		
		BufferedReader br = new BufferedReader(isr);
		if (br == null) return -3;
		
		// Delete existing people first
		Iterable<Key<Person>> allKeys = ofy.query(Person.class).fetchKeys();
		ofy.delete(allKeys);

		int count = 0;
		String line=null;
		try {
			while ((line = br.readLine()) != null) { 
				Integer iType;
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

				String[] fields = line.split(",", 19);
				for (int i=0; i<fields.length; i++) {
					if (fields[i].equals("\\N")) {
						fields[i] = null;
					}
				}

				/*
				Person(Long id, String firstName, String lastName, Integer type,
				Date medFormA, Date medFormB, Date medFormC, String driversLicense,
				Boolean active, String gender, Date dateOfBirth, String homePhone,
				String workPhone, String cellPhone, String address, String city,
				String state, String zip, String email) {
				 */

				if (fields[3].equals("1")) {
					iType = 1;
				} else {
					iType = 0;
				}

				Date aDate;
				try {
					aDate = df.parse(fields[4]);
				} catch (Exception e3) {
					aDate = null;
				}

				Date bDate;
				try {
					bDate = df.parse(fields[5]);
				} catch (Exception e2) {
					bDate = null;
				}

				Date cDate;
				try {
					cDate = df.parse(fields[6]);
				} catch (Exception e1) {
					cDate = null;
				}

				Boolean bActive;
				if (fields[8].equals("1")) {
					bActive = true;
				} else {
					bActive = false;
				}

				Date dDate;
				try {
					dDate = df.parse(fields[10]);
				} catch (Exception e) {
					dDate = null;
				}

				Person p = new Person(null, fields[1], fields[2], iType, aDate, bDate, cDate,
						fields[7], bActive, fields[9], dDate, fields[11], fields[12], fields[13], fields[14],
						fields[15], fields[16], fields[17], fields[18], null);

				ofy.put(p);
				++count;

			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();


		}

		return count;
	}

	@Override
	public EventSummary addEvent(Event e) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.put(e);
		
		EventSummary es = new EventSummary(e.getId(), e.getEventName());
		
		return es;
	}

	@Override
	public VehicleSummary addVehicle(Vehicle v) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.put(v);
		
		Person p = ofy.get(Person.class, v.getOwner());
		
		VehicleSummary vs = new VehicleSummary(v.getId(), v.getModelYear(), v.getMake(), v.getModel(), p.getFullname());
		return vs;
	}

	@Override
	public Long deleteEvent(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.delete(Event.class, key);
		
		return key;
		
	}

	@Override
	public Long deleteVehicle(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.delete(Vehicle.class, key);
		
		return key;
		
	}

	@Override
	public List<EventSummary> getAllEvents() {
		Objectify ofy = ObjectifyService.begin();
		List<EventSummary> list = new ArrayList<EventSummary>();
		
		Query<Event> allEvents = ofy.query(Event.class).order("departureDate");
		
		for (Event e : allEvents) {
			list.add(new EventSummary(e.getId(), e.getEventName()));
		}
		
		
		return list;
	}

	@Override
	public List<VehicleSummary> getAllVehicles() {
		Objectify ofy = ObjectifyService.begin();
		List<VehicleSummary> list = new ArrayList<VehicleSummary>();
		HashMap<Long,String>ownerNames = new HashMap<Long,String>();
		
		Query<Person> allPeople = ofy.query(Person.class);
		
		for (Person p : allPeople) {
			ownerNames.put(p.getId(), p.getFullname());
		}
		
		
		Query<Vehicle> allVehicles = ofy.query(Vehicle.class);
		for (Vehicle v : allVehicles) {
			list.add(new VehicleSummary(v.getId(), v.getModelYear(), v.getMake(), v.getModel(), ownerNames.get(v.getOwner())));
		}
		
		return list;
	}

	@Override
	public Event getEvent(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		Event e = ofy.get(Event.class, key);
		
		return e;
	}

	@Override
	public Vehicle getVehicle(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		Vehicle v = ofy.get(Vehicle.class, key);
		
		return v;
	}

	/***
	 * Used only by the Admin screens.  Create a user with no validation or confirmation
	 */
	@Override
	public Boolean createLogin(String userName, String password, String realName, String emailAddress, Boolean enabled) {
		Boolean success;
		String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
		User user;

		user = new User(null, userName, hashedPassword, realName, emailAddress);
		user.setEnabled(enabled);
		success = checkAndAddUser(user);
		
		
		return success;
		
	}
	
	/**
	 * Given a User, add the user to the database unless the username is already in use
	 * 
	 * @param newUser - User to add
	 * @return true if user added, false on duplicate username
	 */
	private Boolean checkAndAddUser(User newUser) {
		Objectify ofy = ObjectifyService.begin();
		Boolean success;
		
		User oldUser = ofy.query(User.class).filter("userName", newUser.getUserName()).get();
		if (oldUser != null) {
			// User already exists
			success = false;
		} else {
			ofy.put(newUser);
			success = true;
		}
		
		return success;
		
	}

	@Override
	public void doLogout() {
		HttpSession session = this.getThreadLocalRequest().getSession();
		session.setAttribute("userId", null);
		
	}
	
	@Override
	public Long doLogin(String user, String pass) {
		Objectify ofy = ObjectifyService.begin();
		Boolean success;
		Logger logger = Logger.getLogger("us.mooresr.webtroopmanager");
		
		// If there are no users at all in the database then initialize with an admin user
		List<UserSummary> allUsers = getAllUsers();
		if ((allUsers == null) || (allUsers.size() == 0)) {
			initializeUserDatabase();
		}
		
		logger.fine("Getting user record for userName " + user);
		User u = ofy.query(User.class).filter("userName", user).get();
		if (u == null) {
			// User not found
			logger.fine("Query failed, user not found");
			success = false;
		} else {
			logger.fine("User found, checking password");
			// Check password
			if (BCrypt.checkpw(pass, u.getHashedPassword())) {
				logger.fine("Password matches");
				success = true;
			} else {
				logger.fine("Password mismatch");
				success = false;
			}
		}
		
		// Check if user is enabled
		if (success == true) {
			Boolean enabled = u.getEnabled();
			// Next line handles migration of old users that don't yet have an Enabled value
			if (enabled == null) {
				u.setEnabled(true);
				enabled = true;
			}
			if (enabled == false) {
				logger.fine("User is not enabled");
				success = false;
			}
		}
		
		HttpSession session = this.getThreadLocalRequest().getSession();
		if (success) {
			session.setAttribute("userId", u.getUserId());
		} else {
			session.setAttribute("userId", null);
		}
		
		if (success) {
			return u.getUserId();
		} else {
			return null;
		}

		
	}

	@Override
	public Set<Privilege> getUserPrivileges(Long userId) {
		Objectify ofy = ObjectifyService.begin();
		HashSet<Privilege> privileges = new HashSet<Privilege>();
		
		User user = ofy.get(User.class, userId);
		Set<Long> roleIds = user.getRoleIds();
		if (roleIds != null) {
			Map<Long, Role> roleMap = ofy.get(Role.class, roleIds);
			for (Long roleId : roleIds) {
				Role role = roleMap.get(roleId);
				for (Privilege p : role.getPrivileges()) {
					privileges.add(p);
				}
			}
		}
		
		return privileges;
		
	}
	
	/**
	 * Initialize user database
	 * This code will:
	 *  - Delete all users and roles 
	 *  - Create a user "admin" with a password of "admin"
	 *  - Create a role called "Admin" with all privileges
	 *  - Give the user "admin" the "Admin" role
	 * 
	 */
	@Override
	public void initializeUserDatabase() {
		Objectify ofy = ObjectifyService.begin();
		
		Iterable<Key<User>> allUserKeys = ofy.query(User.class).fetchKeys();
		ofy.delete(allUserKeys);
		
		Iterable<Key<Role>> allRoleKeys = ofy.query(Role.class).fetchKeys();
		ofy.delete(allRoleKeys);
		
		createLogin("admin", "admin", "Admin", "chrismoore@surewest.net", true);
		
		Role adminRole = new Role("Admin");
		for (Privilege p : Privilege.values()) {
			adminRole.addPrivilege(p);
		}
		ofy.put(adminRole);
		
		User u = ofy.query(User.class).filter("userName", "admin").get();
		u.addRole(adminRole.getId());
		ofy.put(u);
		
	}

	@Override
	public List<PersonSummary> getAllAdults() {
		List<PersonSummary> list = new ArrayList<PersonSummary>();
		Objectify ofy = ObjectifyService.begin();
		
		
		Query<Person> allPeople = ofy.query(Person.class).order("lastName");
		
		for (Person p : allPeople) {
			// Type==1 is an adult
			// TODO: This should be encapsulated somehow
			if (p.getType() == 1) {
				list.add(new PersonSummary(p.getId(), p.getFirstName(), p.getLastName()));
			}
		}
		
		
		return list;
	}

	@Override
	public List<EventDriverSummary> getEventDrivers(Long key) {
		Objectify ofy = ObjectifyService.begin();
		List<EventDriverSummary> list = new ArrayList<EventDriverSummary>();
		
		Query<EventDriver> drivers = ofy.query(EventDriver.class).filter("eventId", key);
		for (EventDriver d : drivers) {
			String description;
			Vehicle vehicle = ofy.get(Vehicle.class, d.getVehicleId());
			Person person = ofy.get(Person.class, d.getPersonId());
			description = person.toString() + " driving " + vehicle.toString();
			EventDriverSummary ds = new EventDriverSummary(d.getId(), d.getEventId(), d.getVehicleId(),
					d.getPersonId(), description);
			list.add(ds);
		}
		
		return list;
	}

	@Override
	public List<EventParticipantSummary> getEventParticipants(Long key) {
		Objectify ofy = ObjectifyService.begin();
		List<EventParticipantSummary> list = new ArrayList<EventParticipantSummary>();
		
		Query<EventParticipant> participants = ofy.query(EventParticipant.class).filter("eventId", key);
		for (EventParticipant ep : participants) {
			list.add(new EventParticipantSummary(ep.getId(), ep.getEventId(), ep.getPersonId(), 
					ep.getPaid(), ep.getPermissionSlip(), null, null));
		}
		
		// TODO:  Catch com.googlecode.objectify.NotFoundException here.  Can happen if the event participant list
		// includes a person that doesn't exist.
		for (EventParticipantSummary eps : list) {
			Long personId = eps.getPersonId();
			Person p = ofy.get(Person.class, personId);
			eps.setLastName(p.getLastName());
			eps.setFirstName(p.getFirstName());
		}
		
		return list;
	}

	@Override
	public Long setEventDrivers(Long key, ArrayList<EventDriverSummary> drivers) {
		/* This is a little tricky.  Some of the drivers in the list might be newly
		 * added, while others might be modified.  In addition, there might be some drivers
		 * that were removed from the event, so they're in the datastore but not in the list.
		 * 
		 * There's probably a clever, efficient way to do the updates.  But for now the
		 * lazy, inefficient way is to first delete all drivers from the event, then add the
		 * ones in the list.
		 */
		
		List<EventDriver> driverList = new ArrayList<EventDriver>();
		
		// Using a transaction here - don't want the delete all to succeed and then have the put fail
		Objectify ofy = ObjectifyService.begin();
		
		Iterable<Key<EventDriver>> keys = ofy.query(EventDriver.class).filter("eventId", key).fetchKeys();

		ofy.delete(keys);
			
		// Make EventParticipants from EventParticipantSummaries.
		for (EventDriverSummary eds : drivers) {
			EventDriver ed = new EventDriver(null, key, eds.getPersonId(), eds.getVehicleId());
			driverList.add(ed);
		}
			
		ofy.put(driverList);
					
		return key;
		
	}

	@Override
	public Long setEventParticipants(Long key, ArrayList<EventParticipantSummary> participants) {
		// This is essentially the same as setEventDrivers - all the comments there apply here as well
		List<EventParticipant> participantList = new ArrayList<EventParticipant>();
				
		// Using a transaction here - don't want the delete all to succeed and then have the put fail
		Objectify ofy = ObjectifyService.begin();
		
		Iterable<Key<EventParticipant>> keys = ofy.query(EventParticipant.class).filter("eventId", key).fetchKeys();

		ofy.delete(keys);
			
		// Make EventParticipants from EventParticipantSummaries.
		for (EventParticipantSummary p : participants) {
			EventParticipant ep = new EventParticipant(null, key, p.getPersonId(), p.getPaid(), p.getPermissionSlip());
			participantList.add(ep);
		}
			
		ofy.put(participantList);
					
		return key;
	}

	@Override
	public EventParticipant getEventParticipant(Long eventId, Long personId) {
		Objectify ofy = ObjectifyService.begin();
		List<EventParticipant> list = new ArrayList<EventParticipant>();
		
		Query<EventParticipant> participants = ofy.query(EventParticipant.class).filter("eventId", eventId).filter("personId", personId);
		if (participants.count() == 0) {
			return null;
		} else {
			return participants.get();
		}
		
	}

	/**
	 * Create a user, enable him, and send email to the admin to say that it was done.
	 */
	@Override
	public Boolean autoApproveRequest(String username, String password,	String realName, String emailAddress) {
		Properties props = new Properties();
		Session session = Session.getDefaultInstance(props, null); 
		Boolean success;

		success = createLogin(username, password, realName, emailAddress, true);

		String msgBody;
		if (success) {
			msgBody = "New user created.\n" +
			"Username: " + username + "\n" +
			"Password: " + password + "\n" +
			"Real Name: " + realName + "\n" +
			"Email:" + emailAddress + "\n";
		} else {
			msgBody = "Failed to create user.\n" +
			"Username: " + username + "\n" +
			"Password: " + password + "\n" +
			"Real Name: " + realName + "\n" +
			"Email:" + emailAddress + "\n";
		}

		try {
			Message msg = new MimeMessage(session);
			msg.setFrom(new InternetAddress("cmoore042@gmail.com", "Web Troop Manager Admin"));
			msg.addRecipient(Message.RecipientType.TO,
					new InternetAddress("chrismoore@surewest.net", "Chris Moore"));
			if (success) {
				msg.setSubject("New Web Troop Manager user created.");
			} else {
				msg.setSubject("Web Troop Manager user creation failed.");
			}
			msg.setText(msgBody);
			Transport.send(msg);

		} catch (Exception e) {
			// ...
		}
		
		return success;
	}




	/**
	 * Send an email to the administrator asking that a new user be created.
	 */
	@Override
	public Boolean emailRequestToAdmin(String username, String password,
			String realName, String emailAddress) {
		
		String msgBody = "Username: " + username + "\n" +
        	"Password: " + password + "\n" +
        	"Real Name: " + realName + "\n" +
        	"Email:" + emailAddress + "\n";
		String subject = "Registration request for Web Troop Manager";
		
		return sendEmailToAdmin(subject, msgBody);

	}

	/**
	 * Create user but don't enable him, send a validation link to the user, and send email to the admin
	 */
	@Override
	public Boolean emailValidationToUser(String username, String password,
			String realName, String emailAddress, String hRef) {
		Boolean success=true;
		String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
		String msgBody;
		String validationChallenge;
		
		// Create a random validation challenge
		char buf[] = new char[12];
		double randomNumber;
		double randomNumberSetup;
		char randomCharacter;
		for (int i=0;i<12;i++) {
			randomNumber = Math.random();
			randomNumberSetup = (randomNumber * 26 + 'a');
			randomCharacter = (char) randomNumberSetup;
			buf[i] = randomCharacter;
		}
		validationChallenge = new String(buf);
		
		User newUser = new User(null, username, hashedPassword, realName, emailAddress);
		newUser.setValidationChallenge(validationChallenge);
		newUser.setEnabled(false);
		success = checkAndAddUser(newUser);
		
		if (success == false) {
			// Failed to add the new user
			msgBody = "Failed to add username " + username + " to the system for " + realName + ", email " + emailAddress;
			sendEmailToAdmin("Web Troop Manager: Failed to add user", msgBody);
		} else {
			// Added user.  Tell the user and the admin about it.
			msgBody = "Added username " + username + " to the system for " + realName + ", email " + emailAddress + ".  Validation email sent.";
			sendEmailToAdmin("Web Troop Manager: Added new user", msgBody);
			
			int hashOffset = hRef.indexOf('#');
			String validateUrl = hRef.substring(0, hashOffset) + "#Validate:" + validationChallenge;
			msgBody = "Validation key is " + validationChallenge + ", url is " + validateUrl;
			sendEmailToUser(newUser, "Vailidate your login to Web Troop manager", msgBody);
			
		}
		
		
		

		return success;
	}
	
	private Boolean sendEmailToAdmin(String subject, String body) {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);       
		Boolean success=true;


        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("cmoore042@gmail.com", "Web Troop Manager Admin"));
            msg.addRecipient(Message.RecipientType.TO,
                             new InternetAddress("chrismoore@surewest.net", "Chris Moore"));
            msg.setSubject(subject);
            msg.setText(body);
            Transport.send(msg);
    
        } catch (AddressException e) {
            success = false;
        } catch (MessagingException e) {
            success = false;
        } catch (UnsupportedEncodingException e) {
			success = false;
		}
        
        return success;
	}
	
	private Boolean sendEmailToUser(User user, String subject, String body) {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);       
		Boolean success=true;


        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("cmoore042@gmail.com", "Web Troop Manager Admin"));
            msg.addRecipient(Message.RecipientType.TO,
                             new InternetAddress(user.getEmailAddress(), user.getRealName()));
            msg.setSubject(subject);
            msg.setText(body);
            Transport.send(msg);
    
        } catch (AddressException e) {
            success = false;
        } catch (MessagingException e) {
            success = false;
        } catch (UnsupportedEncodingException e) {
			success = false;
		}
        
        return success;	
	}

	@Override
	public Boolean validateUser(String validationChallenge) {
		Objectify ofy = ObjectifyService.begin();
		
		Query<User> users = ofy.query(User.class).filter("validationChallenge", validationChallenge);
		if (users.count() == 0) {
			return false;
		}
		
		User user = users.get();
		user.setEnabled(true);
		ofy.put(user);
		
		sendEmailToAdmin("Web Troop Manager user validated", "User " + user.getRealName() + " has been validated.");
		sendEmailToUser(user, "Web Troop Manager registration validated", "Your registration has been validated.  Thank you for registering for Web Troop manager.");

		return true;
	}

	@Override
	public Role addRole(Role r) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.put(r);
		
		return r;
	}

	@Override
	public Long deleteRole(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.delete(Role.class, key);
		
		return key;
		
	}

	@Override
	public List<Role> getAllRoles() {
		Objectify ofy = ObjectifyService.begin();
		List<Role> list = new ArrayList<Role>();
		
		Query<Role> allRoles = ofy.query(Role.class);
		
		for (Role e : allRoles) {
			list.add(new Role(e.getId(), e.getRoleName(), e.getPrivileges()));
		}
		
		
		return list;
	}

	@Override
	public Role getRole(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		Role e = ofy.get(Role.class, key);
		
		return e;
	}

	@Override
	public UserSummary addUser(User u) {
		Objectify ofy = ObjectifyService.begin();
		Logger logger = Logger.getLogger("us.moores.webtroopmanager");
		
		// If we're updating a user we need to look at the provided password and see if it is blank.
		// For an existing user a blank password means "don't change it"
		// If we're adding a new user then the password field needs to be hashed before adding
		
		String newHashedPassword;
		String plainPassword;
		
		plainPassword = u.getHashedPassword();
		
		if (u.getUserId() == null) {
			logger.fine("Adding new user");
			newHashedPassword = BCrypt.hashpw(plainPassword, BCrypt.gensalt());
			logger.fine("Setting hashed password to " + newHashedPassword);
		} else {
			logger.fine("Updating existing user");
			if (plainPassword.length() == 0) {
				User oldUser = ofy.get(User.class, u.getUserId());
				newHashedPassword = oldUser.getHashedPassword();
				logger.fine("New password is blank, keeping old hashed password of " + newHashedPassword);
			} else {
				newHashedPassword = BCrypt.hashpw(plainPassword, BCrypt.gensalt());
				logger.fine("New password is not blank, changing hashed password to " + newHashedPassword);
			}
		}
		
		u.setHashedPassword(newHashedPassword);
		
		ofy.put(u);
		
		UserSummary us = new UserSummary(u.getUserId(), u.getUserName(), u.getRealName());
		
		return us;
	}

	@Override
	public Long deleteUser(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		ofy.delete(User.class, key);
		
		return key;
	}

	@Override
	public List<UserSummary> getAllUsers() {
		Objectify ofy = ObjectifyService.begin();
		List<UserSummary> list = new ArrayList<UserSummary>();
		
		Query<User> allUsers = ofy.query(User.class);
		
		for (User u : allUsers) {
			list.add(new UserSummary(u.getUserId(), u.getUserName(), u.getRealName()));
		}
		
		
		return list;
	}

	@Override
	public User getUser(Long key) {
		Objectify ofy = ObjectifyService.begin();
		
		User u = ofy.get(User.class, key);
		
		return u;
	}

	@Override
	public Set<Long> getMatchingPeople(Long userId) {
		Objectify ofy = ObjectifyService.begin();
		HashSet<Long> people;
		
		User user = ofy.get(User.class, userId);
		people = (HashSet<Long>) user.getPersonIds();

		
		return people;
	}

	@Override
	public Set<Long> getUsersVehicles(Long userId) {
		Objectify ofy = ObjectifyService.begin();
		HashSet<Long> vehicles = new HashSet<Long>();
		
		User user = ofy.get(User.class, userId);
		HashSet<Long> peopleMatchingThisUser = (HashSet<Long>) user.getPersonIds();

		if (peopleMatchingThisUser != null) {
			Query<Vehicle> allVehicles = ofy.query(Vehicle.class);
			for (Vehicle v : allVehicles) {
				if (peopleMatchingThisUser.contains(v.getOwner())) {
					vehicles.add(v.getId());
				}
			}
		}
		
		return vehicles;
	}

	@Override
	public List<EventSummary> getFutureEvents() {
		Objectify ofy = ObjectifyService.begin();
		List<EventSummary> list = new ArrayList<EventSummary>();
		Date yesterday = new Date();
		final long MILLIS_PER_DAY = 86400000L;
		yesterday.setTime(yesterday.getTime() - MILLIS_PER_DAY);
		
		Query<Event> allEvents = ofy.query(Event.class).filter("departureDate >", yesterday).order("departureDate");
		
		for (Event e : allEvents) {
			list.add(new EventSummary(e.getId(), e.getEventName()));
		}
		
		
		return list;
	}


	
}
